/****************************************************************************
 *
 *  ALi (Shanghai) Corporation, All Rights Reserved. 2008 Copyright (C)
 *
 *  File: Usb_upgrade.c
 *
 *  Description: This file contains the definition of ALi STB to STB upgrade.
 *  History:
 *      Date        	Author      	Version  		Comment
 *      ====        	======      =======  	=======
 *  1.  2007.6.30  Shine Zhou  	comext  		Initial
 *
 ****************************************************************************/
#include <types.h>
#include <retcode.h>
#include <sys_config.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <bus/sci/sci.h>
#include <hld/hld_dev.h>
#include <osal/osal_timer.h>
#include <api/libchunk/chunk.h>
#include <api/libc/fastCRC.h>

#include <api/libfs2/stat.h>
#include <api/libfs2/dirent.h>
#include <api/libfs2/statvfs.h>
#include <api/libfs2/unistd.h>
#include <api/libfs2/stdio.h>
#include <api/libupg/usb_upgrade.h>

//#define USBUPG_PRINTF soc_printf
#define USBUPG_PRINTF(...) do{}while(0)
USBUPG_FILENODE *usbupg_filelist=NULL;
UINT16 usbupg_node_num = 0;
CHUNK_HEADER *usbupg_blockinfo=NULL;
UINT16 usbupg_block_num = 0;
UINT8 usbupg_bloader = 0;


/*
    Get real upgrade chunk list
*/
int usb_get_upg_chunklist(CHUNK_LIST* chunk_list,UINT32* fiter_chunk_array,UINT8 flter_cnt)
{
	CHUNK_LIST* plist;
	unsigned long id;
	unsigned char name[16];
	unsigned long count,temp_cnt;
	unsigned long addr;
	unsigned long i;
	unsigned long j;	
	temp_cnt = 0;
	struct sto_device *sto = NULL;
	sto = (struct sto_device *)dev_get_by_id(HLD_DEV_TYPE_STO, 0);
	ASSERT(sto != NULL);

	if(!(chunk_list&&fiter_chunk_array)) return 0;

	count = sto_chunk_count(0, 0);
	if(!count) return 0;

	plist = chunk_list;

	for(i=1; i<=count; i++)
	{
	addr = sto_chunk_goto(&id, 0, i);
	if(addr & 0x80000000) return 0;

	sto_get_data(sto, name, addr + CHUNK_NAME, 16);

	for(j = 0;j<flter_cnt;j++)
	{
		if(fiter_chunk_array[j] == id)
		{
		break;
		}
	}
	if(j >=flter_cnt)
		{
		temp_cnt++;
		plist->id = id;

		plist->upg_flag = 0;

		plist++;
		}

	}

    return temp_cnt;
}
RET_CODE usbupg_block_init(UINT16 *block_num)
{
	UINT16 i,block_cnt,block_cnt_upg;
	CHUNK_LIST *chunk_tmp_list;
	UINT32 filter_chunk[2] = {CADATA_ID,0};
	
	block_cnt = sto_chunk_count(0,0);
	if(block_cnt <= 0 )
	{
		*block_num = 0;
		return RET_FAILURE;
	}
	else
	{
		chunk_tmp_list = MALLOC(sizeof(CHUNK_LIST) * block_cnt);
		if(chunk_tmp_list == NULL)
		{
			return RET_FAILURE;
		}
	}

	MEMSET((UINT8*)chunk_tmp_list,0,sizeof(CHUNK_LIST) * block_cnt);
	block_cnt_upg = usb_get_upg_chunklist(chunk_tmp_list,filter_chunk,sizeof(filter_chunk)/sizeof(filter_chunk[0]));
	
	usbupg_blockinfo = (CHUNK_HEADER *)MALLOC(sizeof(CHUNK_HEADER) * block_cnt_upg);
	if(usbupg_blockinfo == NULL)
	{
		if(chunk_tmp_list)
			{
			free(chunk_tmp_list);
			}
		return RET_FAILURE;
	}
	else
	{
		usbupg_block_num = block_cnt_upg;
		*block_num = (block_cnt_upg + 1);
		
		for(i = 0;i < block_cnt_upg;i++)
		{
			sto_get_chunk_header((chunk_tmp_list + i)->id,(usbupg_blockinfo + i));
		}
		if(chunk_tmp_list)
		{
			free(chunk_tmp_list);
		}
	}
	
	usbupg_filelist = (USBUPG_FILENODE *)MALLOC(sizeof(USBUPG_FILENODE) * USBUPG_MAX_FILENUM_SUPPORT);
	if(usbupg_filelist == NULL)
	{
		usbupg_block_num = 0;
		free(usbupg_blockinfo);
		usbupg_blockinfo = NULL;
		return RET_FAILURE;
	}
	else
	{
		usbupg_node_num = 0;//initlize node buffer
		MEMSET(usbupg_filelist,0x0, sizeof(USBUPG_FILENODE) * USBUPG_MAX_FILENUM_SUPPORT);
	}

#ifndef UPG_MEMCFG_SUPPORT    
    //not show memcfg upg in menu
    if((ALI_S3503==sys_ic_get_chip_id())||(ALI_S3821==sys_ic_get_chip_id()))
    {
        UINT16 temp_block_num = *block_num;
        *block_num = temp_block_num - 1;
        usbupg_block_num -= 1;

#ifndef ALICAST_SUPPORT
        	for(i = 2;i < block_cnt-1;i++)
		{
		    memcpy((usbupg_blockinfo+i), (usbupg_blockinfo+i+1), sizeof(CHUNK_HEADER));
		}        

#else
		//change for ALiCast
		//there is a new chunk "HDCP2.0", memcfg will be 4th chunk in ALiCast 
		for(i = 3; i <block_cnt-1; i++)
		{
		    memcpy((usbupg_blockinfo+i), (usbupg_blockinfo+i+1), sizeof(CHUNK_HEADER));
		}
#endif


		
    }
#endif        

	return RET_SUCCESS;
}

RET_CODE usbupg_get_blockinfo(UINT16 block_idx,char *str_blockname,UINT32 *pchunk_offset)
{
	RET_CODE ret;

	ret = RET_FAILURE;
	if(block_idx == 0)
	{
		sprintf(str_blockname,"%s","AllCode");
		*pchunk_offset = SYS_FLASH_SIZE;
		if(usbupg_bloader==0)
		{// not include bootloader
			*pchunk_offset -= usbupg_blockinfo[0].offset;
		}
			
		ret = RET_SUCCESS;
	}
	else if(block_idx < usbupg_block_num)
	{
		memcpy(str_blockname,(usbupg_blockinfo + block_idx - 1)->name,16);
		*pchunk_offset = (usbupg_blockinfo + block_idx - 1)->offset;//block length,include chunk header
		ret = RET_SUCCESS;
	}
	else
	{
		memcpy(str_blockname,(usbupg_blockinfo + block_idx - 1)->name,16);
		*pchunk_offset = (usbupg_blockinfo + block_idx - 1)->len + CHUNK_NAME;//block length,include chunk header
		ret = RET_SUCCESS;
	}

	return ret;
}

RET_CODE usbupg_block_free()
{
	if(usbupg_filelist != NULL)
	{
		usbupg_node_num = 0;//initlize node buffer
		free(usbupg_filelist);
		usbupg_filelist = NULL;
	}
	if(usbupg_blockinfo!= NULL)
	{
		usbupg_block_num = 0;//initlize node buffer
		free(usbupg_blockinfo);
		usbupg_blockinfo= NULL;
	}

	return RET_SUCCESS;
}

BOOL b_fast_upgrade;//ChengYu@20140221, for fast upgrade

RET_CODE usbupg_get_file_filter(UINT16 block_idx, UINT8 *upgfile_extends, UINT8 *upgfile_filters)
{
	UINT32 id_filter;

	if(block_idx == 0)//all code upgrade
	{
		if (b_fast_upgrade)//ChengYu@20140221, for fast upgrade
			id_filter = USBUPG_FAST_ID;
		else
			id_filter = USBUPG_ALLCODE_ID;
	}
	else
		id_filter = ((usbupg_blockinfo + block_idx - 1)->id) & 0xFFFF0000;

	switch(id_filter)
	{
		case USBUPG_LOGO_ID:
			sprintf(upgfile_filters,"%s","");
			sprintf(upgfile_extends,"%s","M2V");
			break;
		case USBUPG_DFLTDB_ID:
			sprintf(upgfile_filters,"%s","DDB");//default db
			sprintf(upgfile_extends,"%s","BIN");
			break;
		case USBUPG_USERDB_ID:
			sprintf(upgfile_filters,"%s","UDB");//user db
			sprintf(upgfile_extends,"%s","BIN");
			break;
		case USBUPG_KEYDATA_ID:
			sprintf(upgfile_filters,"%s","KDT");//user db
			sprintf(upgfile_extends,"%s","BIN");
			break;
		case USBUPG_MAINCODE_ID:
			sprintf(upgfile_filters,"%s","MCD");
			sprintf(upgfile_extends,"%s","BIN");
			break;
		case USBUPG_SEECODE_ID:
			sprintf(upgfile_filters,"%s","SCD");
			sprintf(upgfile_extends,"%s","BIN");
			break;
		case USBUPG_OTALOADER_ID:
			sprintf(upgfile_filters,"%s","SLD");
			sprintf(upgfile_extends,"%s","BIN");
			break;
		case USBUPG_BOOTLOADER_ID:
			sprintf(upgfile_filters,"%s","BLD");
			sprintf(upgfile_extends,"%s","ABS");
			break;
		case USBUPG_ALLCODE_ID:
#if (AOV_PRODUCT_NAME == DTB276)
			sprintf(upgfile_filters,"%s","DTB");
#else
			sprintf(upgfile_filters,"%s","ALL");
#endif
			sprintf(upgfile_extends,"%s","ABS");
			break;
		case USBUPG_FAST_ID:
/*Martin@20150212*/
			//sprintf(upgfile_filters,"%s","FAL");	
#if (AOV_PRODUCT_NAME == DTB276)
			sprintf(upgfile_filters,"%s","DTB");
#else
			sprintf(upgfile_filters,"%s","ALL");
#endif
//----End Martin
			sprintf(upgfile_extends,"%s","ABS");
			break;
#ifdef HDCP_IN_FLASH
		case USBUPG_HDCPKEY_ID:

#ifndef ALICAST_SUPPORT
			sprintf(upgfile_filters,"%s","");
			sprintf(upgfile_extends,"%s","KEY");
#else
			//change for ALiCast
			//HDCP key 2.0 is named as HDCP20_XXXXX.key
			sprintf(upgfile_filters,"%s","HDCP20");
			sprintf(upgfile_extends,"%s","KEY");
#endif
			
			break;
#endif
#ifdef UPG_MEMCFG_SUPPORT
        case USBUPG_MEMCFG_ID:
            sprintf(upgfile_filters,"%s","");
			sprintf(upgfile_extends,"%s","ABS");
			break;
#endif            
		default:
			sprintf(upgfile_filters,"%s","");//user db
			sprintf(upgfile_extends,"%s","ABS");
			break;
	}

    return RET_SUCCESS;
}

extern UINT8 sd_mount;
RET_CODE usbupg_create_filelist(UINT16 block_idx,UINT16* usbupg_nodenum)
{
	UINT16 str_len;
	int dir_fd;
	struct dirent *dent;
	char usbupg_fsbuff[sizeof(struct dirent) + FILE_NAME_SIZE];
	char upgfile_extends[3 + 1];
#ifndef ALICAST_SUPPORT
	char upgfile_filters[3 + 1];
#else
	char upgfile_filters[6 + 1];
#endif

	
	USBUPG_BLOCK_TYPE block_type;

    block_type = usbupg_get_block_type(block_idx);
	usbupg_get_file_filter(block_idx, upgfile_extends, upgfile_filters);

	//dir_fd = fs_opendir("/c");
	if(sd_mount)
		dir_fd = fs_opendir("/mnt/sda1");
	else
		dir_fd = fs_opendir("/mnt/uda1");

	if(dir_fd < 0)/*USB disk root dir open failure*/
	{
		*usbupg_nodenum = 0;
		return RET_FAILURE;
	}

	dent = ( struct dirent *) usbupg_fsbuff;
	usbupg_node_num = 0;//initlize node buffer
	MEMSET(usbupg_filelist,0x0, sizeof(USBUPG_FILENODE) * USBUPG_MAX_FILENUM_SUPPORT);
	while(1)
	{

		if (fs_readdir(dir_fd,dent) <= 0)
			break;/*Tail of the dir files*/
		
		if(S_ISDIR(dent->d_type))
		{
			continue;
		}
		else
		{
			str_len = STRLEN(dent->d_name);
			if(!strncasecmp((dent->d_name + str_len - 3),upgfile_extends,3))
			{
#ifdef USBUPG_UPGFILE_FILTER				
				if((STRLEN(upgfile_filters) == 3)
					&& (strncasecmp(dent->d_name,upgfile_filters,3) != 0 ))
					continue;//filter failure
#endif/*Using prefix name to distiguish different upgrade type,eg. all*.abs for allcode*/

#ifdef ALICAST_SUPPORT
				//Both  HDCP KEY1.1 and 2.0 got HDCP20 as filter and .key as extend
				//But HDCP KEY 1.1 should not be named as HDCP20_XXXXX.key while HDCP KEY 2.0 is named as HDCP2.0_XXX.key
				if(block_idx == 2)//HDCP KEY 1.1 //SKIP key name which contains filter
				{
					if((STRLEN(upgfile_filters) == 6)//add for alicast
						&& (strncasecmp(dent->d_name,upgfile_filters,6) == 0 ))
						continue;//filter failure
				}
				else if(block_idx ==3)//HDCP KEY 2.0
				{
					if((STRLEN(upgfile_filters) == 6)//add for alicast
						&& (strncasecmp(dent->d_name,upgfile_filters,6) != 0 ))
						continue;//filter failure				
				
				}
#endif
				
				usbupg_filelist[usbupg_node_num].upg_file_type = block_type;
				STRCPY((usbupg_filelist[usbupg_node_num].upg_file_name),dent->d_name);
				usbupg_node_num++;

				if(usbupg_node_num > USBUPG_MAX_FILENUM_SUPPORT)
					break;//check usbupg files if overflow
			}
		}
	}

	fs_closedir(dir_fd);
	*usbupg_nodenum = usbupg_node_num;
	if(usbupg_node_num == 0)
		return RET_FAILURE;
	else
		return RET_SUCCESS;
}

RET_CODE usbupg_get_filenode(PUSBUPG_FILENODE usbupg_node,UINT16 usbupg_nodeidx)
{
	if(usbupg_node_num == 0)
	{
		return RET_FAILURE;
	}
	else
	{
		MEMCPY(usbupg_node,(usbupg_filelist+usbupg_nodeidx),sizeof(USBUPG_FILENODE));
		return RET_SUCCESS;
	}
}

RET_CODE usbupg_readfile(UINT16 block_idx,UINT16 usbupg_fileidx,void *file_buffer,INT32 buffer_size,usbupg_prgrs_disp progress_disp)
{
	char dir_str[USBUPG_FILE_NAME_LENGTH];
	UINT32 i,fileblocks_num,chunk_crc,chunk_len;
	int file_ret;
	INT32 file_length;
	void *adr_bak;
	FILE *fp_handle;

	if(usbupg_node_num == 0)
	{
		return USBUPG_FILEREAD_ERROR_NOFILE;
	}
	else
	{
		if(0 == sd_mount)
			sprintf(dir_str,"/mnt/uda1/%s",usbupg_filelist[usbupg_fileidx].upg_file_name);		
		else
			sprintf(dir_str,"/c/%s",usbupg_filelist[usbupg_fileidx].upg_file_name);

		fp_handle = fopen(dir_str,"rb");
		if(fp_handle == NULL)
			return USBUPG_FIELREAD_ERROR_READERROR;

		adr_bak = file_buffer;
		memset(adr_bak,0xff,buffer_size);//clear buffer
		if(block_idx > 1)//non-allcode upgrade,should reserve a CHUNK_HEADER size
		{
			file_buffer = file_buffer + USBUPG_CHUNK_SIZE;
			buffer_size= buffer_size - USBUPG_CHUNK_SIZE;
		}

		file_ret = fseek(fp_handle,0,SEEK_END);
		file_length = (unsigned long)ftell(fp_handle);

		if((file_length < 0) || (file_ret < 0))
		{
			fclose(fp_handle);
			return USBUPG_FIELREAD_ERROR_READERROR;
		}
		else
		{
			if(block_idx == 0)	// allcode upgrade
			{
				if(usbupg_bloader == 0)
				{//skip bootloader
					file_length -= usbupg_blockinfo[0].offset;
				}
			}
			if(file_length<0)
			{
				fclose(fp_handle);
				return USBUPG_FIELREAD_ERROR_READERROR;
			}
			
			if(file_length > buffer_size)
			{
				fclose(fp_handle);
				return USBUPG_FIELREAD_ERROR_OVERFLOW;
			}
			else
			{
				if((file_length % SECTOR_SIZE) == 0)
					fileblocks_num = file_length/SECTOR_SIZE;
				else
					fileblocks_num = file_length/SECTOR_SIZE + 1;

				if(block_idx == 0 && usbupg_bloader == 0)
				{//skip bootloader
					fseek(fp_handle,usbupg_blockinfo[0].offset,SEEK_SET);//set pos at head
				}
				else
				{
					fseek(fp_handle,0,SEEK_SET);//set pos at head
				}
				
				for(i = 0;i < fileblocks_num;i++)
				{
					file_ret = fread(file_buffer,SECTOR_SIZE,1,fp_handle);
					file_buffer = file_buffer +SECTOR_SIZE;//move buffer pointer

					if(progress_disp != NULL)
					{
						progress_disp(i *100 / fileblocks_num);
					}

					if(file_ret < 0)
					{
						fclose(fp_handle);
						return USBUPG_FIELREAD_ERROR_READERROR;
					}
				}//read a sector size each time

				if(progress_disp != NULL)
					progress_disp(100);
				if(block_idx != 0)
				{
					if(block_idx != 1)//except BOOTLOADER,for bootloader no crc etc.
					{
						memcpy(adr_bak,(usbupg_blockinfo + block_idx - 1), sizeof(CHUNK_HEADER));
						chunk_len = (file_length + USBUPG_CHUNKCRC_OFFSET);
//					#ifdef HDCP_IN_FLASH
						chunk_crc = (NO_CRC == (usbupg_blockinfo + block_idx - 1)->crc) ? NO_CRC: MG_Table_Driven_CRC(0xFFFFFFFF,(adr_bak + 4*sizeof(UINT32)), chunk_len);
//					#else
//						chunk_crc = MG_Table_Driven_CRC(0xFFFFFFFF,(adr_bak + 4*sizeof(UINT32)), chunk_len);
//					#endif
						store_long((adr_bak + CHUNK_ID),(usbupg_blockinfo + block_idx - 1)->id);
						store_long((adr_bak + CHUNK_LENGTH),chunk_len);
						store_long((adr_bak + CHUNK_OFFSET),(usbupg_blockinfo + block_idx - 1)->offset);
						store_long((adr_bak + CHUNK_CRC),chunk_crc);
						/*length include all file data length and part of the chunk head,except for id,len,offset,crc*/
					}
					else//for bootloader special case
					{
						//TODO:should get system time and set in CHUNK_HEADER??
						memcpy((adr_bak + 4*sizeof(UINT32)),(usbupg_blockinfo + block_idx - 1)->name,(sizeof(CHUNK_HEADER) - 4*sizeof(UINT32)));
					}
				}
				
				fclose(fp_handle);

				if(1)
				{
					char *pointer = adr_bak;
					//char * buffer;
					UINT32 crc=0,data_id=0,data_crc=0;
					INT32 data_len=0,data_off=0;
					
					do{
						data_id  = fetch_long((unsigned char*)(pointer + CHUNK_ID));
						data_len = fetch_long((unsigned char*)(pointer + CHUNK_LENGTH));
						data_off = fetch_long((unsigned char*)(pointer + CHUNK_OFFSET));
						data_crc = fetch_long((unsigned char*)(pointer + CHUNK_CRC));

						if((data_off==0)||((UINT32)pointer ==(UINT32)(adr_bak + buffer_size)))
							break;
						if (pointer != 0)
						{
							if ((data_id >> 16) & 0xFF + (data_id >> 24) & 0xFF != 0xFF)
								return USBUPG_FIELREAD_ERROR_READERROR;
						}
						
						if(block_idx > 1)//non-allcode upgrade,should reserve a CHUNK_HEADER size
						{
							buffer_size= buffer_size + USBUPG_CHUNK_SIZE;
						}
						
						if (data_off > buffer_size)
						{
							return USBUPG_FIELREAD_ERROR_READERROR;
						}
						
						if ((data_off != 0) && (data_len > data_off))
						{
							return USBUPG_FIELREAD_ERROR_READERROR;
						}
#if ((!defined(FTA_ONLY))&&(!defined(_CAS9_CA_ENABLE_))&&(!defined(CA_DVT)))//0//ndef _CAS9_CA_ENABLE_		//no need to  check CRC because maincode/seecode will be verified	
						if (data_crc != NO_CRC)
						{
#if 0 // Need not to malloc a buffer for checking crc 
							if ((buffer = (UINT8 *)malloc(data_len)) == NULL) 
								ASSERT(0);
							memcpy( buffer, pointer + 16, data_len);
#endif
							crc = MG_Table_Driven_CRC(0xFFFFFFFF, pointer + 16, data_len);
							//free(buffer);
							if (data_crc != crc)
							{
								return USBUPG_FIELREAD_ERROR_READERROR;
							}
						}
#endif
						pointer += data_off;
						if ((UINT32)pointer >(UINT32)(adr_bak + buffer_size))
						{
							return USBUPG_FIELREAD_ERROR_READERROR;
						}
					}
					while(1);
				}
				return USBUPG_FILEREAD_NO_ERROR;
			}
		}			
	}
}

static BOOL all_data_is_0xff(UINT8 *addr, UINT32 len)
{
    UINT32 i;
    for (i = 0; i < len; i++)
    {
        if (addr[i] != 0xff)
            return FALSE;
    }
    return TRUE;
}

BOOL usbupg_burnflash(UINT16 block_idx,UINT8 *buffer_adr,UINT32 buffer_size,usbupg_prgrs_disp progress_disp)
{
	UINT8* malloc_sector_buff;	//read flash data to sdram,to compare if need write
	UINT32 param;	
	INT32 f_block_addr,burn_size;
	struct sto_device *f_dev;
	BOOL need_erase;

	burn_size = (INT32)buffer_size;
	if(progress_disp != NULL)
		progress_disp(0);
	if(block_idx == 0)	// allcode upgrade
	{
		if(usbupg_bloader==0)
		{// not include bootloader, skip loader
			f_block_addr = usbupg_blockinfo[0].offset;
		}
		else
			f_block_addr = 0x0;
	}
	else if(block_idx == 1)	//bootloader
		f_block_addr = 0x0;
	else
		f_block_addr = (INT32)sto_chunk_goto(&((usbupg_blockinfo + block_idx - 1)->id), 0xFFFFFFFF, 1);
	
	if ((f_dev = (struct sto_device *)dev_get_by_id(HLD_DEV_TYPE_STO, 0)) == NULL)
	{
		USBUPG_PRINTF("ERROR: Not found Flash device!\n");
		return FALSE;
	}
	if (sto_open(f_dev) != SUCCESS)
	{
		USBUPG_PRINTF("ERROR: sto_open failed!\n");
		return FALSE;
	}
	if ((malloc_sector_buff = (UINT8*)MALLOC(SECTOR_SIZE)) == NULL)
	{
		USBUPG_PRINTF("ERROR: malloc failed!\n");
		return FALSE;
	}
	
	if((f_block_addr % SECTOR_SIZE) != 0
		|| (buffer_size % SECTOR_SIZE) !=0)
	{
		INT32 head_sector_adr,tail_sector_adr,usbupg_blk_endadr;
		INT32 memcpy_src_adr,memcpy_dst_adr;
		UINT32 memcpy_len;

		head_sector_adr = f_block_addr - f_block_addr % SECTOR_SIZE;//get last sector head adr
		tail_sector_adr = (f_block_addr + buffer_size + SECTOR_SIZE) - (f_block_addr + buffer_size)%SECTOR_SIZE;
		usbupg_blk_endadr = f_block_addr + buffer_size;
		
		while(head_sector_adr < tail_sector_adr)
		{//write a sector,using malloc_sector_buff
			if (sto_lseek(f_dev, head_sector_adr, STO_LSEEK_SET) != head_sector_adr)
			{
				USBUPG_PRINTF("ERROR: sto_lseek failed!\n");
				FREE(malloc_sector_buff);
				return FALSE;
			}
			if (sto_read(f_dev, malloc_sector_buff, SECTOR_SIZE) != SECTOR_SIZE)
			{
				USBUPG_PRINTF("ERROR: sto_read failed!\n");
				FREE(malloc_sector_buff);
				return FALSE;
			}

			need_erase = !all_data_is_0xff(malloc_sector_buff, SECTOR_SIZE);
			/*copy upgrade data here*/
			if(head_sector_adr <= f_block_addr)
			{
				memcpy_len = (head_sector_adr + SECTOR_SIZE - f_block_addr);
				if(memcpy_len > buffer_size)
					memcpy_len = buffer_size;

				memcpy_src_adr = (INT32)buffer_adr;
				memcpy_dst_adr = (INT32)(malloc_sector_buff + f_block_addr - head_sector_adr);
				memcpy((void *)memcpy_dst_adr,(void *)memcpy_src_adr,memcpy_len);

				buffer_adr = buffer_adr + memcpy_len;
				buffer_size = buffer_size - memcpy_len;
			}
			else
			{
				if(buffer_size >= SECTOR_SIZE)
					memcpy_len = SECTOR_SIZE;
				else
					memcpy_len = buffer_size;

				memcpy_src_adr = (INT32)buffer_adr;
				memcpy_dst_adr = (INT32)malloc_sector_buff;
				memcpy((void *)memcpy_dst_adr,(void *)memcpy_src_adr,memcpy_len);

				buffer_adr = buffer_adr + memcpy_len;
				buffer_size = buffer_size - memcpy_len;
			}

			if (need_erase)
			{
#if 0				
				/* Uper 22 bits of MSB is start offset based on SYS_FLASH_BASE_ADDR */
				//param = head_sector_adr;
				//param = param << 10;	
				/* Lower 10 bits of LSB is length in K bytes*/
				//param += SECTOR_SIZE >> 10;
				USBUPG_PRINTF("sto_io_control ereser sector param%x\n",param);
				//if(sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE, param) != SUCCESS)
				UINT32 tmp_param[2];
				tmp_param[0] = head_sector_adr;;
				tmp_param[1] = SECTOR_SIZE>>10 ;
				param= (UINT32)tmp_param;

			if(sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE_EXT, param) != SUCCESS)//Summic add for check the >=4M Flash
				{
					USBUPG_PRINTF("ERROR: erase flash memory failed!\n");
					FREE(malloc_sector_buff);
					return FALSE;
				}
#else
				UINT32 tmp_param[2];
				tmp_param[0] = head_sector_adr;
				tmp_param[1] = SECTOR_SIZE >> 10;
				param = (UINT32)tmp_param;
				if(sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE_EXT, param) != SUCCESS)
				{
					USBUPG_PRINTF("ERROR: erase flash memory failed!\n");
					FREE(malloc_sector_buff);
					return FALSE;
				}
#endif				
			}
			if (!all_data_is_0xff(malloc_sector_buff, SECTOR_SIZE))
			{
				if (sto_lseek(f_dev,head_sector_adr,STO_LSEEK_SET) != head_sector_adr)
				{
					USBUPG_PRINTF("ERROR: sto_lseek failed!\n");
					FREE(malloc_sector_buff);
					return FALSE;
				}	
				if(sto_write(f_dev,(UINT8 *)malloc_sector_buff, SECTOR_SIZE)!=SECTOR_SIZE)
				{
					USBUPG_PRINTF("ERROR: sto_write failed!\n");
					FREE(malloc_sector_buff);
					return FALSE;
				}
			}
			head_sector_adr += SECTOR_SIZE;
			
			if(progress_disp != NULL)
				progress_disp((burn_size -buffer_size)*100/burn_size);

			if(buffer_size == 0)
				break;
		}
	}//for block upgrade
	else
	{
		if (sto_lseek(f_dev, f_block_addr, STO_LSEEK_SET) != f_block_addr)
		{
			USBUPG_PRINTF("ERROR: sto_lseek failed!\n");
			FREE(malloc_sector_buff);
			return FALSE;
		}
		if (sto_read(f_dev, malloc_sector_buff, SECTOR_SIZE) != SECTOR_SIZE)
		{
			USBUPG_PRINTF("ERROR: sto_read failed!\n");
			FREE(malloc_sector_buff);
			return FALSE;
		}
		
		while(buffer_size)
		{
			sto_get_data(f_dev, malloc_sector_buff, f_block_addr, SECTOR_SIZE);
			if(mem_cmp(malloc_sector_buff, buffer_adr, SECTOR_SIZE)==FALSE)
			{
				need_erase = !all_data_is_0xff(malloc_sector_buff, SECTOR_SIZE);
				if (need_erase)
				{
#if 0					
					/* Uper 22 bits of MSB is start offset based on SYS_FLASH_BASE_ADDR */
					//param = f_block_addr;
					//param = param << 10;	
					/* Lower 10 bits of LSB is length in K bytes*/
					//param += SECTOR_SIZE >> 10;
					//if(sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE, param) != SUCCESS)
					UINT32 tmp_param[2];
					tmp_param[0] = f_block_addr;
					tmp_param[1] = SECTOR_SIZE>>10;
					param= (UINT32)tmp_param;
					if(sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE_EXT, param) != SUCCESS)//Summic add for check the >=4M Flash
					{
						USBUPG_PRINTF("ERROR: erase flash memory failed!\n");
						FREE(malloc_sector_buff);
						return FALSE;
					}
#else
					UINT32 tmp_param[2];
					tmp_param[0] = f_block_addr;
					tmp_param[1] = SECTOR_SIZE >> 10;
					param = (UINT32)tmp_param;
					if(sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE_EXT, param) != SUCCESS)
					{
						USBUPG_PRINTF("ERROR: erase flash memory failed!\n");
						FREE(malloc_sector_buff);
						return FALSE;
					}
#endif					
				}
				if (!all_data_is_0xff(buffer_adr, SECTOR_SIZE))
				{
					if (sto_lseek(f_dev,(INT32)f_block_addr,STO_LSEEK_SET) != (INT32)f_block_addr)
					{
						USBUPG_PRINTF("ERROR: sto_lseek failed!\n");
						FREE(malloc_sector_buff);
						return FALSE;
					}	
					if(sto_write(f_dev, buffer_adr, SECTOR_SIZE)!=SECTOR_SIZE)
					{
						USBUPG_PRINTF("ERROR: sto_write failed!\n");
						FREE(malloc_sector_buff);
						return FALSE;
					}
				}
			}
			
			f_block_addr +=SECTOR_SIZE;
			buffer_adr +=SECTOR_SIZE;
			buffer_size -= SECTOR_SIZE;
			if(progress_disp != NULL)
				progress_disp((burn_size -buffer_size)*100/burn_size);
		}
	}

	if(progress_disp != NULL)
		progress_disp(100);
	FREE(malloc_sector_buff);
	return TRUE;
}

USBUPG_BLOCK_TYPE usbupg_get_block_type(UINT16 block_idx)
{
	UINT32 id_filter;
	USBUPG_BLOCK_TYPE block_type;

	if(block_idx == 0)//all code upgrade
		id_filter = 0xFFFF0000;
	else
		id_filter = ((usbupg_blockinfo + block_idx - 1)->id) & 0xFFFF0000;
	
	switch(id_filter)
	{
		case USBUPG_LOGO_ID:
			block_type = USBUPG_LOGO;
			break;
		case USBUPG_DFLTDB_ID:
			block_type = USBUPG_DFLTDB;
			break;
		case USBUPG_USERDB_ID:
			block_type = USBUPG_USERDB;
			break;
#ifdef HDCP_IN_FLASH
		case USBUPG_HDCPKEY_ID	:
			block_type = USBUPG_HDCPKEY;
			break;
#endif
		case USBUPG_KEYDATA_ID:
			block_type = USBUPG_KEYDB;
			break;
		case USBUPG_MAINCODE_ID:
			block_type = USBUPG_MAINCODE;
			break;
		case USBUPG_SEECODE_ID:
			block_type = USBUPG_SEECODE;
			break;
		case USBUPG_OTALOADER_ID:
			block_type = USBUPG_SECONDLOADER;
			break;
		case USBUPG_BOOTLOADER_ID:
			block_type = USBUPG_BOOTLOADER;
			break;
		case USBUPG_ALLCODE_ID:
			block_type = USBUPG_ALLCODE;
			break;
#ifdef UPG_MEMCFG_SUPPORT            
        case USBUPG_MEMCFG_ID:
			block_type = USBUPG_MEMCFG;
			break;
#endif
		default:
			block_type = USBUPG_OTHER;
			break;
	}

	return block_type;
}

RET_CODE usbupg_dump_flash(UINT32 block_idx,UINT8 *str_file,usbupg_prgrs_disp progress_disp)
{/*block_idx parameter reserved for extended usage*/
	UINT16 i;
	UINT8 *p_buff,str_path[64];
	FILE *fp_dump;
	struct sto_device *f_dev;

	sprintf(str_path,"/c/%s",str_file);
	fp_dump = fopen(str_path,"w");
	f_dev = (struct sto_device *)dev_get_by_id(HLD_DEV_TYPE_STO, 0);
	if ((f_dev == NULL) || (fp_dump == NULL))
	{
		return USBUPG_FILE_NOT_EXIST;
	}
	p_buff = MALLOC(SECTOR_SIZE);
	if(p_buff == NULL)
	{
		fclose(fp_dump);
		return USBUPG_BUFFER_MALLOC_FAILURE;
	}

	progress_disp(0);
    if(0 == block_idx)
    {
	sto_lseek(f_dev,0, STO_LSEEK_SET);
	for(i = 0;i < (SYS_FLASH_SIZE/SECTOR_SIZE);i++)
	{
		MEMSET(p_buff,0x0,SECTOR_SIZE);
		sto_read(f_dev,p_buff,SECTOR_SIZE);
		fwrite(p_buff,1,SECTOR_SIZE, fp_dump);

		progress_disp((i*100)/(SYS_FLASH_SIZE/SECTOR_SIZE));
	}
    }else
    {
    	    CHUNK_HEADER ch_header;
		UINT32 dump_size = 0;
		UINT32 start_addr= sto_chunk_goto(&block_idx,0xFFFFFFFF,1);
    	    sto_get_chunk_header(block_idx, &ch_header);
		dump_size = (ch_header.offset == 0)?ch_header.len +0x10:ch_header.offset;
		dump_size = dump_size - CHUNK_HEADER_SIZE;
		UINT32 read_size = dump_size;
		sto_lseek(f_dev,start_addr+CHUNK_HEADER_SIZE,STO_LSEEK_SET);
		for(i = 0; i< dump_size/SECTOR_SIZE; ++i)
		{
			MEMSET(p_buff,0x0,SECTOR_SIZE);
			if(read_size > SECTOR_SIZE)
			{
				sto_read(f_dev,p_buff,SECTOR_SIZE);
				fwrite(p_buff,1,SECTOR_SIZE, fp_dump);
			}
			else
			{
				sto_read(f_dev,p_buff,read_size);
				fwrite(p_buff,1,read_size, fp_dump);
				break;
			}
			read_size -= SECTOR_SIZE;
			progress_disp((i*100)/(dump_size/SECTOR_SIZE));
		}
    }

	progress_disp(100);
	FREE(p_buff);
	fclose(fp_dump);
	fs_sync("/c");
	return RET_SUCCESS;
}

RET_CODE usbupg_match_file_structure(UINT16 absfile_idx)
{
	char dir_str[USBUPG_FILE_NAME_LENGTH];
	UINT8 i,chunk_cnt;
	UINT32 file_size,buff_offset,chunk_offset;
	FILE *fp_handle;
	CHUNK_HEADER chunk_node;

	if(usbupg_node_num == 0)
	{/*No abs file*/
		return USBUPG_CHUNK_CNT_MISMATCH;
	}
	else
	{
		sprintf(dir_str,"/c/%s",usbupg_filelist[absfile_idx].upg_file_name);	
		fp_handle = fopen(dir_str,"rb");
		if(fp_handle == NULL)
			return USBUPG_FILEREAD_ERROR_NOFILE;

		fseek(fp_handle,0,SEEK_END);
		file_size = ftell(fp_handle);
		if((file_size < SYS_FLASH_SIZE/4) || (file_size > SYS_FLASH_SIZE))
		{
			fclose(fp_handle);
			return USBUPG_FILE_SIZE_MISMATCH;/*Invalid file*/
		}

		buff_offset = 0;
		for(i = 0;i < usbupg_block_num;i++)
		{
			MEMSET(&chunk_node,0x0,sizeof(CHUNK_HEADER));
			fseek(fp_handle,buff_offset,SEEK_SET);
			file_size = fread(&chunk_node,sizeof(CHUNK_HEADER),1,fp_handle);/*actual read file bytes*/
			chunk_offset = fetch_long((unsigned char *)&(chunk_node.offset));
			buff_offset += usbupg_blockinfo[i].offset;

			USBUPG_PRINTF("buff_offset is %x  file %x,flash%x.\n",buff_offset,chunk_offset,usbupg_blockinfo[i].offset);
			if(chunk_offset != usbupg_blockinfo[i].offset)
			{
				fclose(fp_handle);
				return USBUPG_CHUNK_SIZE_MISMATCH;/*Can not directly reference,for little endian in MIPS*/
			}
		}
		
		fclose(fp_handle);
	}

	return RET_SUCCESS;
}

void set_upgrade_bloader_flag(UINT8 flag)
{
    usbupg_bloader = flag;
}

RET_CODE usbupg_allcode_burn(UINT16 absfile_idx,usbupg_prgrs_disp progress_disp)
{
	char dir_str[USBUPG_FILE_NAME_LENGTH];
	UINT8 i,sector_cnt,*p_sector;
	UINT32 file_len,sto_param;
	FILE *fp_handle;
	struct sto_device *f_dev;
	
	sprintf(dir_str,"/c/%s",usbupg_filelist[absfile_idx].upg_file_name);	
	fp_handle = fopen(dir_str,"rb");
	if(fp_handle == NULL)
		return USBUPG_FILEREAD_ERROR_NOFILE;

	p_sector  = MALLOC(SECTOR_SIZE);
	fseek(fp_handle,0,SEEK_END);
	file_len = ftell(fp_handle);
	if((file_len < SYS_FLASH_SIZE/4) || (file_len > SYS_FLASH_SIZE))
	{
		fclose(fp_handle);
		FREE(p_sector);
		return USBUPG_FILE_SIZE_MISMATCH;
	}

	if ((f_dev = (struct sto_device *)dev_get_by_id(HLD_DEV_TYPE_STO, 0)) == NULL)
	{
		USBUPG_PRINTF("ERROR: Not found Flash device!\n");
		FREE(p_sector);
		fclose(fp_handle);
		return USBUPG_FLASH_WR_ERROR;
	}
	
	progress_disp(0);
	if((usbupg_blockinfo[0].offset%SECTOR_SIZE) == 0)  //jump bootloader
		i = (usbupg_blockinfo[0].offset)/SECTOR_SIZE;
	else
		i = 0;
    if(usbupg_bloader)
        i = 0;
	fseek(fp_handle,i*SECTOR_SIZE,SEEK_SET);
	sto_lseek(f_dev,0,STO_LSEEK_SET);	
	sector_cnt = ((file_len % SECTOR_SIZE) == 0)?(file_len>>16):((file_len>>16) + 1);

	for(;i < sector_cnt;i++)
	{
		MEMSET(p_sector,0xFF,SECTOR_SIZE);
		fread(p_sector,sizeof(char),SECTOR_SIZE,fp_handle);

#if 0				
		//sto_param = i*SECTOR_SIZE;
		//sto_param = sto_param << 10;	/* Lower 10 bits of LSB is length in K bytes*/
		//sto_param += SECTOR_SIZE >> 10;
		//sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE, sto_param);
		UINT32 tmp_param[2];
			tmp_param[0] = i*SECTOR_SIZE;
			tmp_param[1] = SECTOR_SIZE>>10;
			sto_param= (UINT32)tmp_param;
			sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE_EXT, sto_param);	//Summic add for check the >=4M Flash
#else
		// fix for >4M flash
		UINT32 tmp_param[2];
		tmp_param[0] = i*SECTOR_SIZE;
		tmp_param[1] = SECTOR_SIZE >> 10;
		sto_param = (UINT32)tmp_param;
		if(sto_io_control(f_dev, STO_DRIVER_SECTOR_ERASE_EXT, sto_param) != SUCCESS)
		{
			USBUPG_PRINTF("ERROR: erase flash memory failed!\n");
			return FALSE;
		}
#endif
		sto_lseek(f_dev,i*SECTOR_SIZE,STO_LSEEK_SET);
		sto_write(f_dev,p_sector, SECTOR_SIZE);

		progress_disp((i*100)/sector_cnt);
	}
	progress_disp(100);

	FREE(p_sector);
	fclose(fp_handle);
	return RET_SUCCESS;
}

static void usbupg_get_block_offset(UINT16 block_idx,UINT32 *p_offset,UINT32 *p_len)
{
	UINT8 i;

	*p_offset = 0;
	if(block_idx == 0)
	{
		*p_len = SYS_FLASH_SIZE;
		return;
	}
	else
	{
		*p_len = 0;
	}
	
	for(i = 0;i < usbupg_block_num;i++)
	{
		if(i == block_idx-1)
		{
			*p_len = usbupg_blockinfo[i].offset;
			break;
		}
		*p_offset += usbupg_blockinfo[i].offset;
	}

	if(block_idx == usbupg_block_num)
	{//user db
		*p_len = usbupg_blockinfo[block_idx-1].len;
	}
}

RET_CODE usbupg_readfile_block(UINT16 block_idx,UINT16 absfile_idx,void *file_buffer,UINT32 buffer_size)
{
	char dir_str[USBUPG_FILE_NAME_LENGTH];
	UINT32 fp_offset,fp_len;
	FILE *fp_handle;
	
	usbupg_get_block_offset(block_idx,&fp_offset,&fp_len);
	if((file_buffer == NULL) || (buffer_size < fp_len))
	{
		return USBUPG_BUFFER_MALLOC_FAILURE;
	}
	
	sprintf(dir_str,"/c/%s",usbupg_filelist[absfile_idx].upg_file_name);	
	fp_handle = fopen(dir_str,"rb");
	if(fp_handle == NULL)
		return USBUPG_FILEREAD_ERROR_NOFILE;
	fseek(fp_handle,fp_offset,SEEK_SET);
	fread(file_buffer,sizeof(char),fp_len,fp_handle);
	fclose(fp_handle);

	return RET_SUCCESS;
}

